/*
 * Copyright (c) 2024, Qi Yaolong.
 * dragon@hbu.edu.cn
 * HeBei University, China.
 * 
 * 简体中文
 * 保留所有权利。除非符合（遵守）以下条款，否则您不得使用此文件：
 * 
 * 禁止任何未经授权的个人或组织以任何形式将本程序及其附属资料、创新、创意、架构设计、算法、衍生作品
 * 等用于任何商业目的、盈利活动、各类竞赛(比赛)、直播教学、录播教学、线下课程、书籍编写、教材编写、
 * 会议、培训、公益活动、项目课题、毕业设计、毕业论文、学术论文等。
 * 
 * 本程序由版权持有人和贡献人“按原样”提供，不提供任何明示或暗示担保，包括但不局限于对适销性和特定
 * 用途适合性的暗示担保。在任何情况下，版权持有人或贡献人对因使用本程序而导致的任何直接、间接、附带、
 * 特殊、典型或后果性损害（包括但不限于购买替代商品或服务；使用损失、数据丢失或利润损失；业务中断）
 * 不承担任何责任，无论其如何产生，也不论其责任理论为何，无论是合同、严格责任还是侵权（包括疏忽或
 * 其他），即使已告知此类损害的可能性。
 * 
 * English
 * All rights reserved. You may NOT use this file except in compliance with the
 * following terms:
 * 
 * IT IS PROHIBITED FOR ANY UNAUTHORIZED PERSON OR ENTITY TO USE THIS PROGRAM AND 
 * ITS ANCILLARY DATA, INNOVATIONS, CONCEPTS, ARCHITECTURE DESIGN, ALGORITHMS, 
 * DERIVED WORKS, ETC., IN ANY FORM FOR COMMERCIAL PURPOSES, PROFIT-MAKING 
 * ACTIVITIES, VARIOUS COMPETITIONS (CONTESTS), LIVE TEACHING, PRE-RECORDED 
 * LESSONS, OFFLINE COURSES, BOOK WRITING, CONFERENCES, TRAININGS, PUBLIC WELFARE 
 * ACTIVITIES, PROJECTS, TOPICS, GRADUATION DESIGNS, DISSERTATIONS, THESES, AND 
 * OTHER APPLICATIONS.
 * 
 * THIS PROGRAM IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS PROGRAM, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "string.h"
#include "securec.h"
#include "errcode.h"
#include "osal_addr.h"
#include "sle_common.h"
#include "sle_device_discovery.h"
#include "sle_errcode.h"
#include "osal_debug.h"
#include "osal_task.h"
#include "ohos_sle_common.h"
#include "ohos_sle_errcode.h"
#include "ohos_sle_ssap_server.h"
#include "ohos_sle_ssap_client.h"
#include "ohos_sle_device_discovery.h"
#include "ohos_sle_connection_manager.h"
#include "sle_server.h"
#include "sle_server_adv.h"
#include "../global_settings.h" 
#if SSD1306_SUPPORT
#include "ssd1306.h" 
#endif

#define NAME_MAX_LENGTH 16

static uint8_t sle_local_name[NAME_MAX_LENGTH] = DATA_NODE_ANNOUNCE_NAME;

#define SLE_CONN_INTV_MIN_DEFAULT 0x64

#define SLE_CONN_INTV_MAX_DEFAULT 0x64

#define SLE_ADV_INTERVAL_MIN_DEFAULT 0xC8

#define SLE_ADV_INTERVAL_MAX_DEFAULT 0xC8

#define SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT 0x1F4

#define SLE_CONN_MAX_LATENCY 0x1F3

#define SLE_ADV_TX_POWER 10

#define SLE_ADV_HANDLE_DEFAULT 1

#define SLE_ADV_DATA_LEN_MAX 251

#define SLE_SERVER_LOG "[sle server]"

static uint16_t sle_set_adv_local_name(uint8_t *adv_data, uint16_t max_len)
{
    errno_t ret;
    uint8_t index = 0;

    uint8_t *local_name = sle_local_name;
    uint8_t local_name_len = sizeof(sle_local_name) - 1;
    printf("%s local_name_len = %d\r\n", SLE_SERVER_LOG, local_name_len);
    printf("%s local_name: ", SLE_SERVER_LOG);
    for (uint8_t i = 0; i < local_name_len; i++)
    {
        printf("0x%02x ", local_name[i]);
    }
    printf("\r\n");
    adv_data[index++] = local_name_len + 1;
    adv_data[index++] = SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME; 
    ret = memcpy_s(&adv_data[index], max_len - index, local_name, local_name_len);
    if (ret != EOK)
    {
        printf("%s [sle_set_adv_local_name] memcpy fail\r\n", SLE_SERVER_LOG);
        return 0;
    }
    return (uint16_t)index + local_name_len;
}

static uint16_t sle_set_adv_data(uint8_t *adv_data)
{
    size_t len = 0;
    uint16_t idx = 0;
    errno_t ret = 0;

    len = sizeof(struct sle_adv_common_value);
    struct sle_adv_common_value adv_disc_level = {
        .length = len - 1,
        .type = SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL, 
        .value = SLE_ANNOUNCE_LEVEL_NORMAL,        
    };
    ret = memcpy_s(&adv_data[idx], SLE_ADV_DATA_LEN_MAX - idx, &adv_disc_level, len);
    if (ret != EOK)
    {
        printf("%s adv_disc_level memcpy fail\r\n", SLE_SERVER_LOG);
        return 0;
    }
    idx += len;

    len = sizeof(struct sle_adv_common_value);
    struct sle_adv_common_value adv_access_mode = {
        .length = len - 1,
        .type = SLE_ADV_DATA_TYPE_ACCESS_MODE, 
        .value = 0,                            
    };
    ret = memcpy_s(&adv_data[idx], SLE_ADV_DATA_LEN_MAX - idx, &adv_access_mode, len);
    if (ret != EOK)
    {
        printf("%s adv_access_mode memcpy fail\r\n", SLE_SERVER_LOG);
        return 0;
    }
    idx += len;

    return idx;
}

static uint16_t sle_set_scan_response_data(uint8_t *scan_rsp_data)
{
    uint16_t idx = 0;
    errno_t ret;
    size_t scan_rsp_data_len = sizeof(struct sle_adv_common_value);

    struct sle_adv_common_value tx_power_level = {
        .length = scan_rsp_data_len - 1,
        .type = SLE_ADV_DATA_TYPE_TX_POWER_LEVEL, 
        .value = SLE_ADV_TX_POWER,                
    };
    ret = memcpy_s(scan_rsp_data, SLE_ADV_DATA_LEN_MAX, &tx_power_level, scan_rsp_data_len);
    if (ret != EOK)
    {
        printf("%s sle scan response data memcpy fail\r\n", SLE_SERVER_LOG);
        return 0;
    }
    idx += scan_rsp_data_len;

    idx += sle_set_adv_local_name(&scan_rsp_data[idx], SLE_ADV_DATA_LEN_MAX - idx);
    return idx;
}

static int sle_set_default_announce_param(void)
{
    errno_t ret;
    SleAnnounceParam param = {0}; 
    uint8_t index;

    unsigned char local_addr[SLE_ADDR_LEN] = {0x78, 0x70, 0x60, 0x88, 0x96, 0x45};
    random_mac_addr(local_addr); 
    printf("AnnounceParam.ownAddr: %02x:%02x:%02x:%02x:%02x:%02x\r\n",
           local_addr[0], local_addr[1], local_addr[2], local_addr[3], local_addr[4], local_addr[5]);
#if SSD1306_SUPPORT
    ssd1306_SetCursor(0, 10);
    ssd1306_printf(Font_7x10, White, "%02X:%02X:%02X:%02X:%02X:%02X", local_addr[0], local_addr[1],
                   local_addr[2], local_addr[3], local_addr[4], local_addr[5]);
#endif

    param.announceMode = SLE_ANNOUNCE_MODE_CONNECTABLE_SCANABLE;

    param.announceHandle = SLE_ADV_HANDLE_DEFAULT;

    param.announceGtRole = SLE_ANNOUNCE_ROLE_T_CAN_NEGO;

    param.announceLevel = SLE_ANNOUNCE_LEVEL_NORMAL;

    param.announceChannelMap = SLE_ADV_CHANNEL_MAP_DEFAULT;

    param.announceIntervalMin = SLE_ADV_INTERVAL_MIN_DEFAULT;

    param.announceIntervalMax = SLE_ADV_INTERVAL_MAX_DEFAULT;

    param.connIntervalMin = SLE_CONN_INTV_MIN_DEFAULT;

    param.connIntervalMax = SLE_CONN_INTV_MAX_DEFAULT;

    param.connMaxLatency = SLE_CONN_MAX_LATENCY;

    param.connSupervisionTimeout = SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT;

    param.ownAddr.type = SLE_ADDRESS_TYPE_PUBLIC;

    ret = memcpy_s(param.ownAddr.addr, SLE_ADDR_LEN, local_addr, SLE_ADDR_LEN);
    if (ret != EOK)
    {
        printf("%s [sle_set_default_announce_param] data memcpy fail\r\n", SLE_SERVER_LOG);
        return 0;
    }

    return SleSetAnnounceParam(param.announceHandle, &param);
}

static int sle_set_default_announce_data(void)
{
    errcode_t ret;
    uint8_t announce_data_len = 0;
    uint8_t seek_data_len = 0;
    SleAnnounceData data = {0};                  
    uint8_t adv_handle = SLE_ADV_HANDLE_DEFAULT; 
    uint8_t announce_data[SLE_ADV_DATA_LEN_MAX] = {0};
    uint8_t seek_rsp_data[SLE_ADV_DATA_LEN_MAX] = {0};
    uint8_t data_index = 0;

    announce_data_len = sle_set_adv_data(announce_data);
    data.announceData = announce_data;
    data.announceDataLen = announce_data_len;
    printf("%s data.announce_data_len = %d\r\n", SLE_SERVER_LOG, data.announceDataLen);
    printf("%s data.announce_data: ", SLE_SERVER_LOG);
    for (data_index = 0; data_index < data.announceDataLen; data_index++)
    {
        printf("0x%02x ", data.announceData[data_index]);
    }
    printf("\r\n");

    seek_data_len = sle_set_scan_response_data(seek_rsp_data);
    data.seekRspData = seek_rsp_data;
    data.seekRspDataLen = seek_data_len;
    printf("%s data.seek_rsp_data_len = %d\r\n", SLE_SERVER_LOG, data.seekRspDataLen);
    printf("%s data.seek_rsp_data: ", SLE_SERVER_LOG);
    for (data_index = 0; data_index < data.seekRspDataLen; data_index++)
    {
        printf("0x%02x ", data.seekRspData[data_index]);
    }
    printf("\r\n");

    ret = SleSetAnnounceData(adv_handle, &data);
    if (ret == ERRCODE_SLE_SUCCESS)
    {
        printf("%s [sle_set_default_announce_data] set announce data success.\r\n", SLE_SERVER_LOG);
    }
    else
    {
        printf("%s [sle_set_default_announce_data] set adv param fail.\r\n", SLE_SERVER_LOG);
    }

    return ERRCODE_SLE_SUCCESS;
}

static void sle_announce_enable_cbk(uint32_t announce_id, errcode_t status)
{
    printf("%s [sle_announce_enable_cbk] id:%02x, state:%x\r\n", SLE_SERVER_LOG, announce_id,
           status);
}

static void sle_announce_disable_cbk(uint32_t announce_id, errcode_t status)
{
    printf("%s [sle_announce_disable_cbk] id:%02x, state:%x\r\n", SLE_SERVER_LOG, announce_id,
           status);
}

static void sle_announce_terminal_cbk(uint32_t announce_id)
{
    printf("%s [sle_announce_terminal_cbk] id:%02x\r\n", SLE_SERVER_LOG, announce_id);
}

static void sle_enable_cbk(errcode_t status)
{
    printf("%s [sle_enable_cbk] status:%x\r\n", SLE_SERVER_LOG, status);
    
    sle_enable_server_cbk();
}

static void sle_disable_cbk(errcode_t status)
{
    printf("%s [sle_disable_cbk]\r\n", SLE_SERVER_LOG);
}

errcode_t sle_announce_register_cbks(void)
{
    errcode_t ret;
    SleAnnounceSeekCallbacks seek_cbks = {0};
    seek_cbks.sleAnnounceEnableCb = sle_announce_enable_cbk;     
    seek_cbks.sleAnnounceDisableCb = sle_announce_disable_cbk;   
    seek_cbks.sleAnnounceTerminalCb = sle_announce_terminal_cbk; 
    seek_cbks.sleEnableCb = sle_enable_cbk;                      
    seek_cbks.sleDisableCb = sle_disable_cbk;                    
    ret = SleAnnounceSeekRegisterCallbacks(&seek_cbks);          
    if (ret != ERRCODE_SLE_SUCCESS)
    {
        printf("%s [sle_announce_register_cbks] register_callbacks fail :%x\r\n",
               SLE_SERVER_LOG, ret);
        return ret;
    }
    return ERRCODE_SLE_SUCCESS;
}

errcode_t sle_server_adv_init(void)
{
    errcode_t ret;

    sle_set_default_announce_param();

    sle_set_default_announce_data();

    ret = SleStartAnnounce(SLE_ADV_HANDLE_DEFAULT);
    if (ret != ERRCODE_SLE_SUCCESS)
    {
        printf("%s [sle_server_adv_init] sle_start_announce fail :%x\r\n", SLE_SERVER_LOG, ret);
        return ret;
    }

    return ERRCODE_SLE_SUCCESS;
}
